package ripple.server.core.overlay;

import java.util.Date;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;

import ripple.common.entity.AbstractMessage;
import ripple.common.entity.NodeMetadata;
import ripple.common.entity.ReformMessage;
import ripple.common.entity.ReformRequest;
import ripple.common.tcp.Message;
import ripple.server.core.overlay.tree.TreeOverlay;

public class ReformOverlay implements Overlay {
    
    private Overlay overlay;
    private Overlay newOverlay;
    private String  state;
    private int     term;

    private String overlayType = "tree";
    private int branch;


    private List<NodeMetadata> nodeList;
    private List<NodeMetadata> newNodeList;
    private int proposerId;
    

    

    public Overlay getNewOverlay() {
        return newOverlay;
    }

    public void setNewOverlay(Overlay newOverlay) {
        this.newOverlay = newOverlay;
    }

    

    public int getTerm() {
        return term;
    }

    public void setTerm(int term) {
        this.term = term;
    }




    // state 
    private final static String INITIALZING = "Initializing";
    private final static String NORMAL      = "Normal";
    private final static String CANDIDATE   = "Candidate";
    private final static String LEADER      = "lEADER";
    private final static String FOLLOWER    = "FOLLOWER";
    private final static String COMMITTED   = "Committed";

    // message type
    public final static String PREPARE      = "Prepare";
    public final static String PROPOSE      = "Propose";
    public final static String COMMIT       = "Commit";
    public final static String DEPRECATE    = "Deprecate";

    // action type
    public final static String CONTINUE = "Continue";
    public final static String REJECT   = "Reject";
    public final static String RECORD   = "Record";
    public final static String APPLY_COMMIT = "ApplyCommit";
    public final static String APPLY_DEPRECATE = "ApplyDeprecate";

    public final static String REFORM = "Reform";

    


    public ReformOverlay(int branch) {
        this.setInitializingState();

        this.setBranch(branch);
        this.setOverlayType("tree");
        this.setOverlay(new TreeOverlay(branch));
        this.setTerm(0);
    }

    private String getState() {
        return this.state;
    }

    private void setState(String state) {
        this.state = state;
    }

    public boolean setInitializingState() {
        this.setState(INITIALZING);
        return true;
    }

    public boolean setNormalState() {
        this.setState(NORMAL);
        return true;
    }

    private String getOverlayType() {
        return this.overlayType;
    }

    private void setOverlayType(String overlayType){
        this.overlayType = overlayType;
    }

    private Overlay getOverlay() {
        return this.overlay;
    }

    private void setOverlay(Overlay overlay){
        this.overlay = overlay;
    }

    public List<NodeMetadata> getNodeList() {
        return nodeList;
    }

    public void setNodeList(List<NodeMetadata> nodeList) {
        this.nodeList = nodeList;
        // if (INITIALZING.equals(this.getState())) {
        //     this.nodeList = nodeList;
        // }
    }

    private int getProposerId() {
        return this.proposerId;
    }

    private void setProposerId(int proposerId) {
        this.proposerId = proposerId;
    }

    public int getBranch() {
        return branch;
    }

    public void setBranch(int branch) {
        this.branch = branch;
    }

    @Override
    public void buildOverlay(List<NodeMetadata> nodeList) {
        if (!INITIALZING.equals(this.getState())) {
            return;
        }
        this.setNodeList(nodeList);
        this.overlay.buildOverlay(nodeList);
        this.setNormalState();
    }

    @Override
    public List<NodeMetadata> calculateNodesToSync(AbstractMessage message, NodeMetadata source, NodeMetadata current) {
        return this.overlay.calculateNodesToSync(message, source, current);
    }

    @Override
    public List<NodeMetadata> calculateNodesToCollectAck(AbstractMessage message) {
        return this.overlay.calculateNodesToCollectAck(message);
    }
    
    @Override
    public int getMaxLoad() {
        return this.overlay.getMaxLoad();
    }

    public String reformMessageHandler(ReformMessage reformMessage, int currentId) {
        int sourceId = reformMessage.getLastUpdateServerId();
        switch (reformMessage.getType()) {
            // case PREPARE:
            //     if (NORMAL.equals(this.getState())) {
            //         this.setState(FOLLOWER);
            //         this.setProposerId(sourceId);
            //         return CONTINUE;
            //     } else if (FOLLOWER.equals(this.getState()) && sourceId < this.getProposerId()) {
            //         this.setProposerId(sourceId);
            //         return CONTINUE;
            //     }
            //     break;
            case PROPOSE:
                if (FOLLOWER.equals(this.getState()) && sourceId == this.getProposerId()) {
                    return RECORD;
                }
                break;
            case COMMIT:
                if (FOLLOWER.equals(this.getState()) && sourceId == this.getProposerId()) {
                    return APPLY_COMMIT;
                }
                break;
            case DEPRECATE:
                if (COMMITTED.equals(this.getState()) && sourceId == this.getProposerId()) {
                    this.setState(NORMAL);
                    return APPLY_DEPRECATE;
                }
                break;
            default:
                break;
        }     
        return REJECT;
    }

    public boolean applyCommit(List<NodeMetadata> nodeList) {
        if (!FOLLOWER.equals(this.getState())) {
            return false;
        }
        switch (this.getOverlayType()) {
            case "tree":
                this.setNewOverlay(new TreeOverlay(this.getBranch()));
                this.getNewOverlay().buildOverlay(newNodeList);
                break;
            default:
                break;
        }
        this.setState(COMMITTED);
        return true;
    }

    public boolean applyDeprecate() {
        if (!COMMITTED.equals(this.getState())) {
            return false;
        }
        this.setOverlay(this.getNewOverlay());
        this.setNewOverlay(null);
        this.setState(NORMAL);
        return true;
    }

    public String reform(int id) {
        if (NORMAL.equals(this.getState())) {
            this.setState(FOLLOWER);
            this.setProposerId(id);
            return CONTINUE;
        }
        return REJECT;
    }

    public static String getNewOverlayString(int term, List<NodeMetadata> newNodeList) {
        return term + "-" + NodeMetadata.ListToString(newNodeList);
    }

    public static int getNewOverlayTermByString(String string) {
        return Integer.parseInt(string.split("-")[0]);
    }

    public static List<NodeMetadata> getNewOverlayNodeListByString(String string) {
        return NodeMetadata.ListFromString(string.split("-")[1]);
    }

    public AbstractMessage getRejectMessage(ReformMessage message, int currentId) {
        String application = ReformOverlay.REJECT;
        String key = ReformOverlay.getNewOverlayString(term, newNodeList);
        Date lastUpdate = new Date(System.currentTimeMillis());
        return new ReformMessage(application, key, lastUpdate, currentId);
    }
    
    
}
